/*
 * Universidade Federal Fluminense
 * Instituto de Computação
 * Prof. Pedro Braconnot Velloso
 * Sistemas Distribuídos - Turma 2011/2
 * Author: Gabriel Almeida de Oliveira
 * Description: Server side
 */

#include "server.h"
#include "client.h"
#include <pthread.h>

void server(packet *peers){
	struct sockaddr_in serverAddress;
	int sock;
	unsigned int clientLength;
	char errorString[140], msg[MAX_MSG_SIZE], hostname[MAXHOSTSIZE];

	// Creating the Socket
	if( (sock = socket(AF_INET, SOCK_STREAM, 0) ) < 0) {										// Use SOCK_STREAM for TCP
		snprintf(errorString, sizeof(errorString), "Server: cannot open socket");
		DieWithError(errorString);
	}

	/*
	 * sin_family can be any of:
	 * AF_INET      (ARPA INTERNET PROTOCOLS) - "Most Used"
	 * AF_UNIX      (UNIX INTERNET PROTOCOLS)
	 * AF_ISO       (ISO PROTOCOLS)
	 * AF_NS        (XEROX NETWORK SYSTEM PROTOCOLS)
	 *
	 * sin_port and sin_addr can be any of:
	 * htons 		(HOST TO NETWORK SHORT) - 2 bytes
	 * htonl		(HOST TO NETWORK LONG)  - 4 bytes
	 * ntos			(NETWORK TO HOST SHORT) - 2 bytes
	 * ntol			(NETOWRK TO HOST LONG)  - 4 bytes
	 *
	 * INADDR_ANY   (USE HOST ADDRESS)
	 */
	
	bzero (&serverAddress, sizeof(serverAddress));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddress.sin_port = htons(DEFAULT_PORT);
	
	if( ( bind (sock, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) ) > 0) {
		snprintf(errorString, sizeof(errorString), "Server: bind failed");
		DieWithError(errorString);
	}
	
	printf("Server: Waiting for data on port TCP %u\n", DEFAULT_PORT);
    
    //   Wait to try client connetions
	if ( listen(sock, MAX_PEER_NUMBER) ){
		snprintf (errorString, sizeof(errorString), "Server: Error while listening for connections");
		DieWithError(errorString);
	}

	
	while(1){
		packet* curr;
		int connected;
		struct sockaddr_in clientAddress;
		clientLength = sizeof (clientAddress);
	    printf("Server: Connecting...\n");

	    //Accept to try client connection
	    connected = accept(sock, (struct sockaddr *) &clientAddress, &clientLength);
	    if(connected < 0){
	    	snprintf (errorString, sizeof(errorString), "Server: cannot connect");
			DieWithError(errorString);
		} else printf ("Server: Client %s:%d connected\n",
	           	inet_ntoa (clientAddress.sin_addr),
		        ntohs (clientAddress.sin_port));

		// Buffering
		bzero(msg, sizeof(msg) );

		// Receiving data: msg and level
		if(( read(connected, msg, MAX_MSG_SIZE ) ) < 0 ) {
			printf("Server: cannot receive data \n");
		}
			
		if ( (curr = (packet*) malloc(sizeof(curr)) ) == NULL) {
			snprintf (errorString, sizeof(errorString), "Main: Error allocating Memory");
			DieWithError(errorString);
		}
		memset(curr, 0x0, sizeof(curr));
										
		curr->msg = (char* ) malloc( MAX_MSG_SIZE * sizeof(char) );
		curr->level = atoi( strtok( msg, ";") );
		curr->msg = strtok( NULL, ";");
		curr->address = clientAddress;
		curr->hash = calculate_hash(curr->msg);
		printf("his hash is: %s\n\n", curr->hash);
		
		

//    getnameinfo((struct sockaddr *) &clientAddress, sizeof(clientAddress), hostname, sizeof(hostname), NULL, 0, 0);

			
		printf("Server: message received from %s (IP: %s): %s with level %d\n",
											hostname, 
											inet_ntoa(clientAddress.sin_addr), 
											curr->msg,
											curr->level);		
		close(connected);
	
	
		packet *me = peers;
		if ( (me = search_by_address("0.0.0.0") ) == NULL){
			printf("\nServer: Couldn't find myself in the peer list!\n");
			exit(ERROR_INVALID_HOST);
		}	
		
		struct arguments args;
		packet *list;
		pthread_t client_thread;
		packet *head = getHead();
		
		switch(curr->level){
			case PEER_IN:
				if ( strcmp(inet_ntoa(me->address.sin_addr), inet_ntoa(clientAddress.sin_addr))) {
					append_node(curr);
				}
				if ( !strcmp(inet_ntoa(me->address.sin_addr), inet_ntoa(head->address.sin_addr))){
					for (list=getHead()->next; list!=NULL; list=list->next){
						args.hostname = inet_ntoa(list->address.sin_addr);
						args.peer_list = curr;
						pthread_create(&client_thread,NULL,(void* )client, &args);
					}
				}
				break;
			case PEER_OUT:
				remove_node(curr);
				if (me==getHead()){	
					for (list=getHead()->next; list!=NULL; list=list->next){
						args.hostname = inet_ntoa(list->address.sin_addr);
						args.peer_list = curr;
						pthread_create(&client_thread,NULL,(void* )client, &args);
					}
				}
			case KILL_APP:
				printf("Server: exiting by remote user request\n\n");
				if (me==getHead()){
					for (list=getHead()->next; list!=NULL; list=list->next){
						args.hostname = inet_ntoa(list->address.sin_addr);
						args.peer_list = curr;
						pthread_create(&client_thread,NULL,(void* )client, &args);				
					}
				}	
				remove_node(me);
				close(sock);
				return;	
			default:
				printf("wtf is that? %d\n\n", curr->level);
		}
		char newHash[MAX_MSG_SIZE];
	    char newString[MAX_MSG_SIZE];
	    for (list=getHead(); list!=NULL; list=list->next){
	    	getContribution(list->hash, newString, count_nodes() );
	        strcat(newHash,newString);
	    }
		printf("Our shared Key is: %s\n\n", newHash);
	}
}